#ifndef __M_DB_H__
#define __M_DB_H__
#include "util.hpp"
#include <mutex>
#include <mysql/mysql.h>
#include <cassert>

/*user_table类需要设计6个方法*/
class user_table
{
private:
    MYSQL* _mysql;//MySQL数据库操作句柄
    std::mutex _mutex;//互斥锁
public:
    user_table(
            const std::string& host,
            const std::string& username,
            const std::string& password,
            const std::string& dbname,
            uint16_t port = 3306)
    {
        _mysql = mysql_util::mysql_create(host,username,password,dbname,port);
        assert(_mysql!=nullptr);
    }
    ~user_table()
    {
        mysql_util::mysql_destory(_mysql);
        _mysql==nullptr;
    }
    /*通过用户名获取用户信息*/
    /*返回值为bool，判断是否成功获取，参数username为用户名，输出型参数user用于保存获取到的用户数据*/
    bool select_by_name(const std::string& username,Json::Value& user)
    {
        #define USER_BY_NAME "select id,score,total_count,win_count from user where username='%s';"
        char sql[4096]={0};
        sprintf(sql,USER_BY_NAME,username.c_str());/*将查询表的字符串写入到sql中*/
        /*MYSQL_RES保存查询结果到本地：mysql_store_resul(_mysql)*/
        MYSQL_RES* res=NULL;
        /*查询操作需要使用互斥锁进行保护*/
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql,sql);//执行查询语句
            if(ret==false)
            {
                DLOG("user select_by_name failed!!\n");
                return false;
            }
            //查询成功，将查询结果保存到本地
            res = mysql_store_result(_mysql);
            if(res==NULL)
            {
                DLOG("have no user info!!");
                return false;
            }

        }
        /*保存到本地之后，需要将结果放入到Json::Value user中
        因此，先获取到结果集的行数，然后遍历结果集，将其放入user中*/
        /*获取结果集的行数*/
        int row_num = mysql_num_rows(res);
        if(row_num!=1)
        {
            DLOG("the user information queried is not unique!");
            return false;
        }
        /*遍历结果集,将数据存储在user中,row是列数噢，row[0]为第0列*/
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);/*先转化成长整型，然后转化成json形式的无符号整型*/
        user["username"] = username;
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }

    /*通过用户id获取用户信息*/
    /*返回值为bool，判断是否成功获取，参数id为用户id，输出型参数user用于保存获取到的用户数据*/
    bool select_by_id(uint16_t id,Json::Value& user)
    {
        #define USER_BY_ID "select username,score,total_count,win_count from user where id='%d';"
        char sql[4096]={0};
        sprintf(sql,USER_BY_ID,id);/*将查询表的字符串写入到sql中*/
        /*MYSQL_RES保存查询结果到本地：mysql_store_resul(_mysql)*/
        MYSQL_RES *res=NULL;
        /*查询操作需要使用互斥锁进行保护*/
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql,sql);//执行查询语句
            if(ret==false)
            {
                DLOG("user select_by_name failed!!\n");
                return false;
            }
            //查询成功，将查询结果保存到本地
            res = mysql_store_result(_mysql);
            if(res==NULL)
            {
                DLOG("have no user info!!");
                return false;
            }

        }
        /*保存到本地之后，需要将结果放入到Json::Value user中
        因此，先获取到结果集的行数，然后遍历结果集，将其放入user中*/
        /*获取结果集的行数*/
        int row_num = mysql_num_rows(res);
        if(row_num!=1)
        {
            DLOG("the user information queried is not unique!");
            return false;
        }
        /*遍历结果集,将数据存储在user中,row是列数噢，row[0]为第0列*/
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)id;;/*化成json形式的无符号整型*/
        user["username"] = row[0];
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }

    /*insert:用户注册时，将用户的数据插入到表中，即可注册成功*/
    bool insert(Json::Value& user)
    {
        #define INSERT_USER "insert user values(null,'%s',password('%s'),1000,0,0);"
        /*判断user中的用户数据是否完整*/
        if(user["username"].isNull() || user["password"].isNull())
        {
            DLOG("INPUT PASSWORD OR USERNAME");
            return false;
        }
        /*数据完整，可以进行注册*/
        char sql[4096]={0};
        /*将执行语句的字符串形式放入到sql中*/
        sprintf(sql,INSERT_USER,user["username"].asCString(),user["password"].asCString());
        bool ret = mysql_util::mysql_exec(_mysql,sql);/*执行*/
        if(ret==false)
        {
            ELOG("insert user info failed!!\n");
            return false;
        }
        return true;
    }

    /*登录验证，并获取用户信息*/
    bool login(Json::Value& user)
    {
        /*先判断登录的数据是否完整*/
        if(user["password"].isNull() || user["username"].isNull())
        {
            DLOG("INPUT PASSWORD OR USERNAME");
            return false;
        }
        /*以用户名和密码共同作为查询过滤条件，查询到数据则表示用户密码一致，否则错误*/
        #define LOGIN_USER "select id, score, total_count, win_count from user where username='%s' and password=password('%s');"
        char sql[4096]={0};
        sprintf(sql,LOGIN_USER,user["username"].asCString(),user["password"].asCString());
        /*查询语句执行，查看是否有数据，如果有，也只能有一条数据，如果没数据，说明登录验证不通过*/
        /*查看数据，使用保存结果到本地的函数mysql_store_result()*/
        MYSQL_RES* res=NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql,sql);
            if(ret==false)
            {
                DLOG("user login failed!!\n");
                return false;
            }
            res = mysql_store_result(_mysql);
            if(res==NULL)/*没有数据，登录验证失败*/
            {
                DLOG("have no login user info!!");
                return false;
            }
        }
        /*有数据，获取该用户的其它数据：分数、场次等*/
        int row_num = mysql_num_rows(res);
        if(row_num!=1)
        {
            DLOG("the user information queried is not unique!");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }

    /*胜利时修改分数,传入胜利玩家的id，通过id查找出对应的数据并进行修改*/
    bool win(uint16_t id)
    {
        #define USER_WIN "update user set score=score+30, total_count=total_count+1, win_count=win_count+1 where id=%d;"
        char sql[4096]={0};
        sprintf(sql,USER_WIN,id);
        std::unique_lock<std::mutex> lock(_mutex);
        bool ret =  mysql_util::mysql_exec(_mysql,sql);
        if(ret==false)
        {
            DLOG("update winner info failed!\n");
            return false;
        } 
        return true;
    }

    /*失败时修改分数,传入失败玩家的id，通过id查找出对应的数据并进行修改*/
    bool loser(uint16_t id)
    {
        #define USER_LOSER "update user set score=score-30, total_count=total_count+1 where id=%d;"
        char sql[4096]={0};
        sprintf(sql,USER_LOSER,id);
        std::unique_lock<std::mutex> lock(_mutex);
        bool ret =  mysql_util::mysql_exec(_mysql,sql);
        if(ret==false)
        {
            DLOG("update loser info failed!\n");
            return false;
        } 
        return true;
    }

};
#endif